/*
 * Copyright (c) 2024 Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * -----------------------------------------------------------------------------
 *
 * $Date:       3. October 2024
 * $Revision:   V3.0
 *
 * Project:     SPI Driver for STMicroelectronics STM32 devices
 *
 * -----------------------------------------------------------------------------
 */

/*! \page spi_stm32 SPI

# Revision History

- Version 3.0
  - Initial release

# Requirements

This driver requires the STM32 device specific **HAL** and **STM32CubeMX** (CubeMX) initialization code generator.
The driver instance is mapped to hardware as shown in the table below:

  CMSIS Driver Instance | STM32 Hardware Resource
  :---------------------|:-----------------------
  Driver_SPI1           | SPI1
  Driver_SPI2           | SPI2
  Driver_SPI3           | SPI3
  Driver_SPI4           | SPI4
  Driver_SPI5           | SPI5
  Driver_SPI6           | SPI6
  Driver_SPI7           | SPI7
  Driver_SPI8           | SPI8

# Deviations

This driver has the following deviations from the CMSIS-Driver specification:

__Conceptual__ deviations:
  - CubeMX generated initialization code (function MX_SPIn_Init) already configures
    the peripheral. Power, clocks, pins, and interrupts are enabled after execution
    of initialization that executes in `main.c`.

__Functional__ deviations:
  - Initialize:
    - depends on the code generated by CubeMX.
    - does not initialize the pins.
  - Uninitialize:
    - does not de-initialize the pins.
  - PowerControl:
    - depends on the code generated by CubeMX.
    - low-power mode is not supported by HAL.
  - Send:
    - maximum number of data items supported by HAL with a single send request is 65535.
  - Receive:
    - maximum number of data items supported by HAL with a single receive request is 65535.
  - Transfer:
    - maximum number of data items supported by HAL with a single transfer request is 65535.
  - Control:
    - changes are not effective after this function but when data transfer operation is started
      (for example: changing Phase or Polarity will change clock line state when data operation is started
       and not after this function finishes).
  - GetStatus:
    - mode fault and data lost cannot be detected until data transfer operation is started.
  - SignalEvent:
    - mode fault and data lost detection does not work until data transfer operation is started.

# CubeMX Configuration

This driver requires the following configuration in CubeMX:

  - **clock**: **SPI** peripheral clock.
  - **peripheral**: **SPI** peripheral configured as **Full-Duplex Master/Slave**
    and **Parameter Settings** configured as desired.
  - **pins**: **SPI MISO**, **MOSI**, **SCK** and **NSS pins**.
  - **DMA**: optional **DMA** configuration for transfers.
  - **interrupts**: enabled **SPI interrupts** and **IRQ handlers** that **Call HAL handlers** and
    enabled **DMA interrupts** and **IRQ handlers** that **Call HAL handlers** if **DMA** is used.

> **Notes**
>
> - configuration information in the **MX_Device.h** file is based on CubeMX configuration.
> - **Hardware** or **Software Slave Select** functionality is only available if **NSS pin** is configured
>   in the CubeMX with alternate function **Hardware NSS Signal**.
> - **DMA** configuration can differ between devices series so configure DMA **as required by the used device**.
> - for **DMA** usage on devices with cache, ensure that data buffers for Send and Receive functions
>   are in **non-cacheable memory**, or ensure that memory for send is updated (**cache clean**) before Send function
>   is called and that memory containing received data is updated after the reception finishes (**cache invalidate**).
> - some DMA controllers can only access specific memories, so ensure that proper memory is used for the buffers
>   according to the DMA requirement.

## Example

### Pinout & Configuration tab

  1. In the **Pinout view** window click on a pin and select it's functionality:
       Pin      | Functionality
       :--------|:--------------------:
       PF6      | **SPI5_NSS**
       PF7      | **SPI5_SCK**
       PF8      | **SPI5_MISO**
       PF9      | **SPI5_MOSI**
     \n

  2. Under **Categories**: **Connectivity** select **SPI5**:

     __Mode__:
       - Mode: **Full-Duplex Master**
       - Hardware NSS Signal: **Hardware NSS Output Signal**

     __Configuration__:
       - Parameter Settings: as desired
       - GPIO Settings:
           Pin Name | Signal on Pin | Pin Context..| GPIO output..| GPIO mode                     | GPIO Pull-up/Pull..| Maximum out..| Fast Mode | User Label
           :--------|:-------------:|:------------:|:------------:|:-----------------------------:|:------------------:|:------------:|:---------:|:----------:
           PF6      | SPI5_NSS      | n/a          | n/a          | Alternate Function Push Pull  | No pull-up and no..| **Medium**   | n/a       | .
           PF7      | SPI5_SCK      | n/a          | n/a          | Alternate Function Push Pull  | No pull-up and no..| **Medium**   | n/a       | .
           PF8      | SPI5_MISO     | n/a          | n/a          | Alternate Function Push Pull  | No pull-up and no..| **Medium**   | n/a       | .
           PF9      | SPI5_MOSI     | n/a          | n/a          | Alternate Function Push Pull  | No pull-up and no..| **Medium**   | n/a       | .
         \n

  3. Under **Categories**: **System Core** select **DMA**
     (might be different on other device series, or for some peripherals might be BDMA):

     __Configuration__:
       - DMA1, DMA2:

         Click on **Add** button and add DMA Requests as in table below:
           DMA Request | Stream         | Direction            | Priority
           :----------:|:--------------:|:---------------------|:--------
           SPI5_RX     | DMA1 Stream 0  | Peripheral to Memory | Low
           SPI5_TX     | DMA1 Stream 1  | Memory to Peripheral | Low
         \n

  4. Under **Categories**: **System Core** select **NVIC**:

     __Configuration__:
       - NVIC:
           NVIC Interrupt Table              | Enabled     | Preemption Priority | Sub Priority
           :---------------------------------|:-----------:|:-------------------:|:------------:
           DMA1 stream0 global interrupt     |   checked   | 0                   | 0
           DMA1 stream1 global interrupt     |   checked   | 0                   | 0
           SPI5 global interrupt             | **checked** | 0                   | 0
         \n

       - Code generation:
           Enabled interrupt table           | Select for..| Generate Enable in..| Generate IRQ h.. | Call HAL handler
           :---------------------------------|:-----------:|:-------------------:|:----------------:|:----------------:
           DMA1 stream0 global interrupt     | unchecked   | checked             | checked          | checked
           DMA1 stream1 global interrupt     | unchecked   | checked             | checked          | checked
           SPI5 global interrupt             | unchecked   | checked             | checked          | checked
         \n

### Clock Configuration tab

  1. Configure **To SPI4,5 (MHz)**: **100**

### Project Manager tab

  1. Under **Advanced Settings**:

     __Generated Function Calls__:
       Generate Code | Function Name               | Peripheral Inst..| Do not generate ..| Visibility (Static)
       :-------------|:---------------------------:|:----------------:|:-----------------:|:-------------------:
       checked       | MX_SPI5_Init                | SPI5             | unchecked         | checked

# Troubleshooting

## Compilation errors

### Error:    SPIn peripheral clock frequency is unknown! {#spi_periph_freq_unknown}

  - Reason:   peripheral clock frequency is not determinable from CubeMX configuration.
  - Solution: add **User Constant** with Name **SPIn_PERIPH_CLOCK_FREQ** and Value of respective SPIn **peripheral clock frequency (in Hz)**.
  - Example:  in the **Pinout & Configuration** tab, under **Categories**: **Connectivity** select **SPI1**, 
              under **Configuration**: **User Constants** add **Constant** with Name **SPI1_PERIPH_CLOCK_FREQ** and Value
              of **peripheral clock frequency (in Hz)** as determined from the **Clock Configuration** or device documentation.
*/

/*! \cond */

#include "SPI_STM32.h"

#include "RTE_Components.h"
#include  CMSIS_device_header

#include <string.h>

// Driver Version **************************************************************
                                                //  CMSIS Driver API version           , Driver version
static  const ARM_DRIVER_VERSION driver_version = { ARM_DRIVER_VERSION_MAJOR_MINOR(2,3), ARM_DRIVER_VERSION_MAJOR_MINOR(3,0) };
// *****************************************************************************

// Driver Capabilities *********************************************************
static const ARM_SPI_CAPABILITIES driver_capabilities = {
  0U,                           // Simplex Mode not supported
  1U,                           // TI Synchronous Serial Interface supported
  0U,                           // Microwire Interface not supported
  1U,                           // Signal Mode Fault event: \ref ARM_SPI_EVENT_MODE_FAULT
  0U                            // Reserved (must be zero)
};
// *****************************************************************************

// Compile-time configuration **************************************************

// Configuration depending on MX_Device.h

// Check if at least one peripheral instance is configured in STM32CubeMX
#if    (!defined(MX_SPI1) && \
        !defined(MX_SPI2) && \
        !defined(MX_SPI3) && \
        !defined(MX_SPI4) && \
        !defined(MX_SPI5) && \
        !defined(MX_SPI6) && \
        !defined(MX_SPI7) && \
        !defined(MX_SPI8))
#error  SPI driver requires at least one SPI peripheral configured in STM32CubeMX!

// Check if MX_Device.h version is as required (old version did not have all the necessary information)
#elif  (!defined(MX_DEVICE_VERSION) || (MX_DEVICE_VERSION < 0x01000000U))
#error  SPI driver requires new MX_Device.h configuration, please regenerate MX_Device.h file!

// Check if peripheral clock frequency is defined
#elif   defined(MX_SPI1) && !defined(MX_SPI1_PERIPH_CLOCK_FREQ)
#error  SPI1 peripheral clock frequency is unknown! Consult the driver documentation at https://open-cmsis-pack.github.io/CMSIS-Driver_STM32/latest/spi_stm32.html#spi_periph_freq_unknown

#elif   defined(MX_SPI2) && !defined(MX_SPI2_PERIPH_CLOCK_FREQ)
#error  SPI2 peripheral clock frequency is unknown! Consult the driver documentation at https://open-cmsis-pack.github.io/CMSIS-Driver_STM32/latest/spi_stm32.html#spi_periph_freq_unknown

#elif   defined(MX_SPI3) && !defined(MX_SPI3_PERIPH_CLOCK_FREQ)
#error  SPI3 peripheral clock frequency is unknown! Consult the driver documentation at https://open-cmsis-pack.github.io/CMSIS-Driver_STM32/latest/spi_stm32.html#spi_periph_freq_unknown

#elif   defined(MX_SPI4) && !defined(MX_SPI4_PERIPH_CLOCK_FREQ)
#error  SPI4 peripheral clock frequency is unknown! Consult the driver documentation at https://open-cmsis-pack.github.io/CMSIS-Driver_STM32/latest/spi_stm32.html#spi_periph_freq_unknown

#elif   defined(MX_SPI5) && !defined(MX_SPI5_PERIPH_CLOCK_FREQ)
#error  SPI5 peripheral clock frequency is unknown! Consult the driver documentation at https://open-cmsis-pack.github.io/CMSIS-Driver_STM32/latest/spi_stm32.html#spi_periph_freq_unknown

#elif   defined(MX_SPI6) && !defined(MX_SPI6_PERIPH_CLOCK_FREQ)
#error  SPI6 peripheral clock frequency is unknown! Consult the driver documentation at https://open-cmsis-pack.github.io/CMSIS-Driver_STM32/latest/spi_stm32.html#spi_periph_freq_unknown

#elif   defined(MX_SPI7) && !defined(MX_SPI7_PERIPH_CLOCK_FREQ)
#error  SPI7 peripheral clock frequency is unknown! Consult the driver documentation at https://open-cmsis-pack.github.io/CMSIS-Driver_STM32/latest/spi_stm32.html#spi_periph_freq_unknown

#elif   defined(MX_SPI8) && !defined(MX_SPI8_PERIPH_CLOCK_FREQ)
#error  SPI8 peripheral clock frequency is unknown! Consult the driver documentation at https://open-cmsis-pack.github.io/CMSIS-Driver_STM32/latest/spi_stm32.html#spi_periph_freq_unknown

#else
#define DRIVER_CONFIG_VALID     1
#endif

// Determine peripheral/HAL differences that driver needs to handle

// Determine if HAL does not have extended module

#if  (((defined(__STM32F1xx_HAL_H) || defined(STM32F1xx_HAL_H)) && !defined(STM32F1xx_HAL_SPI_EX_H)) || \
      ((defined(__STM32F2xx_HAL_H) || defined(STM32F2xx_HAL_H)) && !defined(STM32F2xx_HAL_SPI_EX_H)) || \
      ((defined(__STM32F4xx_HAL_H) || defined(STM32F4xx_HAL_H)) && !defined(STM32F4xx_HAL_SPI_EX_H)) || \
      ((defined(__STM32L0xx_HAL_H) || defined(STM32L0xx_HAL_H)) && !defined(STM32L0xx_HAL_SPI_EX_H)) || \
      ((defined(__STM32L1xx_HAL_H) || defined(STM32L1xx_HAL_H)) && !defined(STM32L1xx_HAL_SPI_EX_H)))
#define SPI_VARIANT_NO_HAL_EX           1
#endif

// *****************************************************************************

#ifdef  DRIVER_CONFIG_VALID     // Driver code is available only if configuration is valid

// Macros
// Macro for section for RW info
#ifdef  SPI_SECTION_NAME
#define SPIn_SECTION_(name,n)   __attribute__((section(name #n)))
#define SPIn_SECTION(n)         SPIn_SECTION_(SPI_SECTION_NAME,n)
#else
#define SPIn_SECTION(n)
#endif

// Macro to create spi_ro_info and spi_rw_info (for instances), with NSS pin configured in the STM32CubeMX
#define INFO_W_NSS_DEFINE(n)                                                                                   \
extern  SPI_HandleTypeDef       hspi##n;                                                                       \
static  const PinConfig_t       spi##n##_nss_config = {  MX_SPI##n##_NSS_GPIOx,                                \
                                                         MX_SPI##n##_NSS_GPIO_Pin,                             \
                                                         MX_SPI##n##_NSS_GPIO_AF,                              \
                                                         MX_SPI##n##_NSS_GPIO_PuPd,                            \
                                                         MX_SPI##n##_NSS_GPIO_Speed                            \
                                                      };                                                       \
static        RW_Info_t         spi##n##_rw_info SPIn_SECTION(n);                                              \
static  const RO_Info_t         spi##n##_ro_info    = { &hspi##n,                                              \
                                                        &spi##n##_rw_info,                                     \
                                                         (uint32_t)MX_SPI##n##_PERIPH_CLOCK_FREQ,              \
                                                        &spi##n##_nss_config                                   \
                                                      };

// Macro to create spi_ro_info and spi_rw_info (for instances), without NSS pin configured in the STM32CubeMX
#define INFO_WO_NSS_DEFINE(n)                                                                                  \
extern  SPI_HandleTypeDef       hspi##n;                                                                       \
static        RW_Info_t         spi##n##_rw_info SPIn_SECTION(n);                                              \
static  const RO_Info_t         spi##n##_ro_info    = { &hspi##n,                                              \
                                                        &spi##n##_rw_info,                                     \
                                                         (uint32_t)MX_SPI##n##_PERIPH_CLOCK_FREQ,              \
                                                         NULL                                                  \
                                                      };

// Macro for declaring functions (for instances)
#define FUNCS_DECLARE(n)                                                                                       \
static  int32_t                 SPI##n##_Initialize      (ARM_SPI_SignalEvent_t cb_event);                     \
static  int32_t                 SPI##n##_Uninitialize    (void);                                               \
static  int32_t                 SPI##n##_PowerControl    (ARM_POWER_STATE state);                              \
static  int32_t                 SPI##n##_Send            (const void *data, uint32_t num);                     \
static  int32_t                 SPI##n##_Receive         (void *data, uint32_t num);                           \
static  int32_t                 SPI##n##_Transfer        (const void *data_out, void *data_in, uint32_t num);  \
static  uint32_t                SPI##n##_GetDataCount    (void);                                               \
static  int32_t                 SPI##n##_Control         (uint32_t control, uint32_t arg);                     \
static  ARM_SPI_STATUS          SPI##n##_GetStatus       (void);

// Macro for defining functions (for instances)
#define FUNCS_DEFINE(n)                                                                                                                                                                 \
static  int32_t                 SPI##n##_Initialize      (ARM_SPI_SignalEvent_t cb_event)                      { return SPIn_Initialize  (&spi##n##_ro_info, cb_event); }               \
static  int32_t                 SPI##n##_Uninitialize    (void)                                                { return SPIn_Uninitialize(&spi##n##_ro_info); }                         \
static  int32_t                 SPI##n##_PowerControl    (ARM_POWER_STATE state)                               { return SPIn_PowerControl(&spi##n##_ro_info, state); }                  \
static  int32_t                 SPI##n##_Send            (const void *data, uint32_t num)                      { return SPIn_Send        (&spi##n##_ro_info, data, num); }              \
static  int32_t                 SPI##n##_Receive         (void *data, uint32_t num)                            { return SPIn_Receive     (&spi##n##_ro_info, data, num); }              \
static  int32_t                 SPI##n##_Transfer        (const void *data_out, void *data_in, uint32_t num)   { return SPIn_Transfer    (&spi##n##_ro_info, data_out, data_in, num); } \
static  uint32_t                SPI##n##_GetDataCount    (void)                                                { return SPIn_GetDataCount(&spi##n##_ro_info); }                         \
static  int32_t                 SPI##n##_Control         (uint32_t control, uint32_t arg)                      { return SPIn_Control     (&spi##n##_ro_info, control, arg); }           \
static  ARM_SPI_STATUS          SPI##n##_GetStatus       (void)                                                { return SPIn_GetStatus   (&spi##n##_ro_info); }

// Macro for defining driver structures (for instances)
#define SPI_DRIVER(n)                   \
ARM_DRIVER_SPI Driver_SPI##n = {        \
  SPI_GetVersion,                       \
  SPI_GetCapabilities,                  \
  SPI##n##_Initialize,                  \
  SPI##n##_Uninitialize,                \
  SPI##n##_PowerControl,                \
  SPI##n##_Send,                        \
  SPI##n##_Receive,                     \
  SPI##n##_Transfer,                    \
  SPI##n##_GetDataCount,                \
  SPI##n##_Control,                     \
  SPI##n##_GetStatus                    \
};

// Pin configuration
typedef struct {
  GPIO_TypeDef                 *ptr_port;               // Pointer to pin's port
  uint16_t                      pin;                    // Pin
  uint16_t                      af;                     // Pin's alternate function setting
  uint16_t                      pull;                   // Pin's pull-up/pull-down setting
  uint16_t                      speed;                  // Pin's speed setting
} PinConfig_t;

// Driver status
typedef struct {
  uint32_t                      initialized  : 1;       // Initialized status: 0 - not initialized, 1 - initialized
  uint32_t                      powered      : 1;       // Power status:       0 - not powered,     1 - powered
  uint32_t                      configured   : 1;       // Configured status:  0 - not configured,  1 - configured
  uint32_t                      reserved     : 29;      // Reserved (for padding)
} DriverStatus_t;

// Instance run-time information (RW)
typedef struct {
  ARM_SPI_SignalEvent_t         cb_event;               // Event callback
  DriverStatus_t                drv_status;             // Driver status
  uint32_t                      default_tx_value;       // Default Tx value
} RW_Info_t;

// Instance compile-time information (RO)
// also contains pointer to run-time information
typedef struct {
        SPI_HandleTypeDef      *ptr_hspi;               // Pointer to SPI handle
        RW_Info_t              *ptr_rw_info;            // Pointer to run-time information (RW)
        uint32_t                peri_clock_freq;        // Peripheral clock frequency (in Hz)
  const PinConfig_t            *ptr_nss_pin_config;     // Pointer to NSS pin configuration structure (NULL - if pin was not configured in STM32CubeMX)
} RO_Info_t;

// Information definitions (for instances)
#ifdef MX_SPI1
#ifdef MX_SPI1_NSS_Pin
INFO_W_NSS_DEFINE (1)
#else
INFO_WO_NSS_DEFINE(1)
#endif
#endif
#ifdef MX_SPI2
#ifdef MX_SPI2_NSS_Pin
INFO_W_NSS_DEFINE (2)
#else
INFO_WO_NSS_DEFINE(2)
#endif
#endif
#ifdef MX_SPI3
#ifdef MX_SPI3_NSS_Pin
INFO_W_NSS_DEFINE (3)
#else
INFO_WO_NSS_DEFINE(3)
#endif
#endif
#ifdef MX_SPI4
#ifdef MX_SPI4_NSS_Pin
INFO_W_NSS_DEFINE (4)
#else
INFO_WO_NSS_DEFINE(4)
#endif
#endif
#ifdef MX_SPI5
#ifdef MX_SPI5_NSS_Pin
INFO_W_NSS_DEFINE (5)
#else
INFO_WO_NSS_DEFINE(5)
#endif
#endif
#ifdef MX_SPI6
#ifdef MX_SPI6_NSS_Pin
INFO_W_NSS_DEFINE (6)
#else
INFO_WO_NSS_DEFINE(6)
#endif
#endif
#ifdef MX_SPI7
#ifdef MX_SPI7_NSS_Pin
INFO_W_NSS_DEFINE (7)
#else
v(7)
#endif
#endif
#ifdef MX_SPI8
#ifdef MX_SPI8_NSS_Pin
INFO_W_NSS_DEFINE (8)
#else
INFO_WO_NSS_DEFINE(8)
#endif
#endif

// List of available SPI instance infos
static const RO_Info_t * const spi_ro_info_list[] = {
#ifdef MX_SPI1
  &spi1_ro_info,
#endif
#ifdef MX_SPI2
  &spi2_ro_info,
#endif
#ifdef MX_SPI3
  &spi3_ro_info,
#endif
#ifdef MX_SPI4
  &spi4_ro_info,
#endif
#ifdef MX_SPI5
  &spi5_ro_info,
#endif
#ifdef MX_SPI6
  &spi6_ro_info,
#endif
#ifdef MX_SPI7
  &spi7_ro_info,
#endif
#ifdef MX_SPI8
  &spi8_ro_info,
#endif
  NULL
};

// Local functions prototypes
static const RO_Info_t         *SPI_GetInfo         (const SPI_HandleTypeDef * const hspi);
static uint32_t                 SPIn_GetPeriphClock (const RO_Info_t * const ptr_ro_info);
static ARM_DRIVER_VERSION       SPI_GetVersion      (void);
static ARM_SPI_CAPABILITIES     SPI_GetCapabilities (void);
static int32_t                  SPIn_Initialize     (const RO_Info_t * const ptr_ro_info, ARM_SPI_SignalEvent_t cb_event);
static int32_t                  SPIn_Uninitialize   (const RO_Info_t * const ptr_ro_info);
static int32_t                  SPIn_PowerControl   (const RO_Info_t * const ptr_ro_info, ARM_POWER_STATE state);
static int32_t                  SPIn_Send           (const RO_Info_t * const ptr_ro_info, const void *data, uint32_t num);
static int32_t                  SPIn_Receive        (const RO_Info_t * const ptr_ro_info,       void *data, uint32_t num);
static int32_t                  SPIn_Transfer       (const RO_Info_t * const ptr_ro_info, const void *data_out, void *data_in, uint32_t num);
static uint32_t                 SPIn_GetDataCount   (const RO_Info_t * const ptr_ro_info);
static int32_t                  SPIn_Control        (const RO_Info_t * const ptr_ro_info, uint32_t control, uint32_t arg);
static ARM_SPI_STATUS           SPIn_GetStatus      (const RO_Info_t * const ptr_ro_info);

// Local driver functions declarations (for instances)
#ifdef MX_SPI1
FUNCS_DECLARE(1)
#endif
#ifdef MX_SPI2
FUNCS_DECLARE(2)
#endif
#ifdef MX_SPI3
FUNCS_DECLARE(3)
#endif
#ifdef MX_SPI4
FUNCS_DECLARE(4)
#endif
#ifdef MX_SPI5
FUNCS_DECLARE(5)
#endif
#ifdef MX_SPI6
FUNCS_DECLARE(6)
#endif
#ifdef MX_SPI7
FUNCS_DECLARE(7)
#endif
#ifdef MX_SPI8
FUNCS_DECLARE(8)
#endif

// Auxiliary functions

/**
  \fn          RO_Info_t *SPI_GetInfo (const SPI_HandleTypeDef * const hspi)
  \brief       Get pointer to RO_Info_t structure corresponding to specified hspi.
  \param[in]   hspi     Pointer to SPI handle structure (SPI_HandleTypeDef)
  \return      pointer to SPI RO info structure (RO_Info_t)
*/
static const RO_Info_t *SPI_GetInfo (const SPI_HandleTypeDef * const hspi) {
  const RO_Info_t *ptr_ro_info;
        uint8_t    i;

  ptr_ro_info = NULL;
  i            = 0U;

  // Find SPI which uses same hspi handle as parameter hspi
  for (i = 0U; i < (sizeof(spi_ro_info_list)/sizeof(RO_Info_t *)); i++) {
    if (spi_ro_info_list[i] != NULL) {
      if (spi_ro_info_list[i]->ptr_hspi == hspi) {
        ptr_ro_info = spi_ro_info_list[i];
        break;
      }
    }
  }

  return ptr_ro_info;
}

/**
  \fn          uint32_t SPIn_GetPeriphClock (const RO_Info_t * const ptr_ro_info)
  \brief       Get peripheral clock frequency.
  \param[in]   ptr_ro_info     Pointer to SPI RO info structure (RO_Info_t)
  \return      frequency in Hz
*/
static uint32_t SPIn_GetPeriphClock (const RO_Info_t * const ptr_ro_info) {
  return ptr_ro_info->peri_clock_freq;
}

// Driver functions ************************************************************

/**
  \fn          ARM_DRIVER_VERSION SPI_GetVersion (void)
  \brief       Get driver version.
  \return      \ref ARM_DRV_VERSION
*/
static ARM_DRIVER_VERSION SPI_GetVersion (void) {
  return driver_version;
}

/**
  \fn          ARM_SPI_CAPABILITIES SPI_GetCapabilities (void)
  \brief       Get driver capabilities.
  \return      \ref ARM_SPI_CAPABILITIES
*/
static ARM_SPI_CAPABILITIES SPI_GetCapabilities (void) {
  return driver_capabilities;
}

/**
  \fn          int32_t SPIn_Initialize (const RO_Info_t * const ptr_ro_info, ARM_SPI_SignalEvent_t cb_event)
  \brief       Initialize SPI Interface.
  \param[in]   ptr_ro_info     Pointer to SPI RO info structure (RO_Info_t)
  \param[in]   cb_event        Pointer to \ref ARM_SPI_SignalEvent
  \return      \ref execution_status
*/
static int32_t SPIn_Initialize (const RO_Info_t * const ptr_ro_info, ARM_SPI_SignalEvent_t cb_event) {

  // Clear run-time info
  memset((void *)ptr_ro_info->ptr_rw_info, 0, sizeof(RW_Info_t));

  // Register callback function
  ptr_ro_info->ptr_rw_info->cb_event = cb_event;

  // Set driver status to initialized
  ptr_ro_info->ptr_rw_info->drv_status.initialized = 1U;

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t SPIn_Uninitialize (const RO_Info_t * const ptr_ro_info)
  \brief       De-initialize SPI Interface.
  \param[in]   ptr_ro_info     Pointer to SPI RO info structure (RO_Info_t)
  \return      \ref execution_status
*/
static int32_t SPIn_Uninitialize (const RO_Info_t * const ptr_ro_info) {

  if (ptr_ro_info->ptr_rw_info->drv_status.powered != 0U) {
    // If peripheral is powered, power off the peripheral
    (void)SPIn_PowerControl(ptr_ro_info, ARM_POWER_OFF);
  }

  // Clear run-time info
  memset((void *)ptr_ro_info->ptr_rw_info, 0, sizeof(RW_Info_t));

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t SPIn_PowerControl (const RO_Info_t * const ptr_ro_info, ARM_POWER_STATE state)
  \brief       Control SPI Interface Power.
  \param[in]   ptr_ro_info     Pointer to SPI RO info structure (RO_Info_t)
  \param[in]   state           Power state
  \return      \ref execution_status
*/
static int32_t SPIn_PowerControl (const RO_Info_t * const ptr_ro_info, ARM_POWER_STATE state) {

  switch (state) {
    case ARM_POWER_FULL:
      if (ptr_ro_info->ptr_rw_info->drv_status.initialized == 0U) {
        return ARM_DRIVER_ERROR;
      }

      // Clear default Tx value
      ptr_ro_info->ptr_rw_info->default_tx_value = 0U;

      // Initialize pins, clocks, interrupts and peripheral
      if (HAL_SPI_Init(ptr_ro_info->ptr_hspi) != HAL_OK) {
        return ARM_DRIVER_ERROR;
      }

      // Set driver status to powered
      ptr_ro_info->ptr_rw_info->drv_status.powered = 1U;
      break;

    case ARM_POWER_OFF:

      // If data transfer operation is in progress, abort it
      if (SPIn_GetStatus(ptr_ro_info).busy != 0U) {
        (void)SPIn_Control(ptr_ro_info, ARM_SPI_ABORT_TRANSFER, 0U);
      }

      // De-initialize pins, clocks, interrupts and peripheral
      (void)HAL_SPI_DeInit(ptr_ro_info->ptr_hspi);

      // Set driver status to not powered
      ptr_ro_info->ptr_rw_info->drv_status.powered = 0U;
      break;

    case ARM_POWER_LOW:
      return ARM_DRIVER_ERROR_UNSUPPORTED;

    default:
      return ARM_DRIVER_ERROR_PARAMETER;
  }

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t SPIn_Send (const RO_Info_t * const ptr_ro_info, const void *data, uint32_t num)
  \brief       Start sending data to SPI transmitter.
  \param[in]   ptr_ro_info     Pointer to SPI RO info structure (RO_Info_t)
  \param[in]   data            Pointer to buffer with data to send to SPI transmitter
  \param[in]   num             Number of data items to send
  \return      \ref execution_status
*/
static int32_t SPIn_Send (const RO_Info_t * const ptr_ro_info, const void *data, uint32_t num) {
  HAL_StatusTypeDef send_status;
  int32_t           ret;

  if ((data == NULL) || (num == 0U) || (num > (uint32_t)UINT16_MAX)) {
    // If any parameter is invalid
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (ptr_ro_info->ptr_rw_info->drv_status.configured == 0U) {
    return ARM_DRIVER_ERROR;
  }

  // Start the send
  if (ptr_ro_info->ptr_hspi->hdmatx != NULL) {  // If DMA is used for Tx
    send_status = HAL_SPI_Transmit_DMA(ptr_ro_info->ptr_hspi, (uint8_t *)data, (uint16_t)num);
  } else {                                      // If DMA is not configured (IRQ mode)
    send_status = HAL_SPI_Transmit_IT (ptr_ro_info->ptr_hspi, (uint8_t *)data, (uint16_t)num);
  }

  // Convert HAL status code to CMSIS-Driver status code
  switch (send_status) {
    case HAL_ERROR:
      ret = ARM_DRIVER_ERROR;
      break;

    case HAL_BUSY:
      ret = ARM_DRIVER_ERROR_BUSY;
      break;

    case HAL_OK:
      ret = ARM_DRIVER_OK;
      break;

    case HAL_TIMEOUT:
    default:
      ret = ARM_DRIVER_ERROR;
      break;
  }

  return ret;
}

/**
  \fn          int32_t SPIn_Receive (const RO_Info_t * const ptr_ro_info, void *data, uint32_t num)
  \brief       Start receiving data from SPI receiver.
  \param[in]   ptr_ro_info     Pointer to SPI RO info structure (RO_Info_t)
  \param[out]  data            Pointer to buffer for data to receive from SPI receiver
  \param[in]   num             Number of data items to receive
  \return      \ref execution_status
*/
static int32_t SPIn_Receive (const RO_Info_t * const ptr_ro_info, void *data, uint32_t num) {
  HAL_StatusTypeDef receive_status;
  int32_t           ret;
  uint32_t          i;
  uint8_t          *ptr_u8;
  uint16_t         *ptr_u16;
  uint32_t         *ptr_u32;

  if ((data == NULL) || (num == 0U) || (num > (uint32_t)UINT16_MAX)) {
    // If any parameter is invalid
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (ptr_ro_info->ptr_rw_info->drv_status.configured == 0U) {
    return ARM_DRIVER_ERROR;
  }

  // Since HAL does not support default value for Transmission during Reception,
  // this is emulated by loading receive buffer with default values and providing it
  // to TransmitReceive function as transmit buffer also

  // Fill buffer with default transmit value
  if (ptr_ro_info->ptr_hspi->Init.DataSize <= SPI_DATASIZE_8BIT) {
    ptr_u8 = (uint8_t *)data;
    for (i = 0U; i < num; i++) {
      *ptr_u8 = (uint8_t)ptr_ro_info->ptr_rw_info->default_tx_value;
      ptr_u8++;
    }
  } else if (ptr_ro_info->ptr_hspi->Init.DataSize <= SPI_DATASIZE_16BIT) {
    ptr_u16 = (uint16_t *)data;
    for (i = 0U; i < num; i++) {
      *ptr_u16 = (uint16_t)ptr_ro_info->ptr_rw_info->default_tx_value;
      ptr_u16++;
    }
  } else {
    ptr_u32 = (uint32_t *)data;
    for (i = 0U; i < num; i++) {
      *ptr_u32 = ptr_ro_info->ptr_rw_info->default_tx_value;
      ptr_u32++;
    }
  }

  // Start the reception
  if ((ptr_ro_info->ptr_hspi->hdmatx != NULL) &&    // If DMA is used for Tx and
      (ptr_ro_info->ptr_hspi->hdmarx != NULL)) {    // If DMA is used for Rx
    receive_status = HAL_SPI_TransmitReceive_DMA(ptr_ro_info->ptr_hspi, (uint8_t *)data, (uint8_t *)data, (uint16_t)num);
  } else {                                          // If DMA is not configured (IRQ mode)
    receive_status = HAL_SPI_TransmitReceive_IT (ptr_ro_info->ptr_hspi, (uint8_t *)data, (uint8_t *)data, (uint16_t)num);
  }

  // Convert HAL status code to CMSIS-Driver status code
  switch (receive_status) {
    case HAL_ERROR:
      ret = ARM_DRIVER_ERROR;
      break;

    case HAL_BUSY:
      ret = ARM_DRIVER_ERROR_BUSY;
      break;

    case HAL_OK:
      ret = ARM_DRIVER_OK;
      break;

    case HAL_TIMEOUT:
    default:
      ret = ARM_DRIVER_ERROR;
      break;
  }

  return ret;
}

/**
  \fn          int32_t SPIn_Transfer (const RO_Info_t * const ptr_ro_info, const void *data_out, void *data_in, uint32_t num)
  \brief       Start sending/receiving data to/from SPI transmitter/receiver.
  \param[in]   ptr_ro_info     Pointer to SPI RO info structure (RO_Info_t)
  \param[in]   data_out        Pointer to buffer with data to send to SPI transmitter
  \param[out]  data_in         Pointer to buffer for data to receive from SPI receiver
  \param[in]   num             Number of data items to transfer
  \return      \ref execution_status
*/
static int32_t SPIn_Transfer (const RO_Info_t * const ptr_ro_info, const void *data_out, void *data_in, uint32_t num) {
  HAL_StatusTypeDef transfer_status;
  int32_t           ret;

  if ((data_out == NULL) || (data_in == NULL) || (num == 0U) || (num > (uint32_t)UINT16_MAX)) {
    // If any parameter is invalid
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (ptr_ro_info->ptr_rw_info->drv_status.configured == 0U) {
    return ARM_DRIVER_ERROR;
  }

  // Start the transfer
  if ((ptr_ro_info->ptr_hspi->hdmatx != NULL) &&    // If DMA is used for Tx and
      (ptr_ro_info->ptr_hspi->hdmarx != NULL)) {    // If DMA is used for Rx
    transfer_status = HAL_SPI_TransmitReceive_DMA(ptr_ro_info->ptr_hspi, (uint8_t *)data_out, (uint8_t *)data_in, (uint16_t)num);
  } else {                                          // If DMA is not configured (IRQ mode)
    transfer_status = HAL_SPI_TransmitReceive_IT (ptr_ro_info->ptr_hspi, (uint8_t *)data_out, (uint8_t *)data_in, (uint16_t)num);
  }

  // Convert HAL status code to CMSIS-Driver status code
  switch (transfer_status) {
    case HAL_ERROR:
      ret = ARM_DRIVER_ERROR;
      break;

    case HAL_BUSY:
      ret = ARM_DRIVER_ERROR_BUSY;
      break;

    case HAL_OK:
      ret = ARM_DRIVER_OK;
      break;

    case HAL_TIMEOUT:
    default:
      ret = ARM_DRIVER_ERROR;
      break;
  }

  return ret;
}

/**
  \fn          uint32_t SPIn_GetDataCount (const RO_Info_t * const ptr_ro_info)
  \brief       Get transferred data count.
  \param[in]   ptr_ro_info     Pointer to SPI RO info structure (RO_Info_t)
  \return      number of data items transferred
*/
static uint32_t SPIn_GetDataCount (const RO_Info_t * const ptr_ro_info) {
  uint32_t cnt;
  uint32_t cnt_xferred;

  if (ptr_ro_info->ptr_rw_info->drv_status.powered == 0U) {
    return 0U;
  }

  cnt = 0U;

  if ((ptr_ro_info->ptr_hspi->pRxBuffPtr != NULL) && (ptr_ro_info->ptr_hspi->RxXferSize != 0U)) {
    // If reception was activated
    if (ptr_ro_info->ptr_hspi->hdmarx != NULL) {        // If DMA is used for Rx
      cnt_xferred = __HAL_DMA_GET_COUNTER(ptr_ro_info->ptr_hspi->hdmarx);
    } else {
      cnt_xferred = ptr_ro_info->ptr_hspi->RxXferCount;
    }

    cnt = (uint32_t)ptr_ro_info->ptr_hspi->RxXferSize - cnt_xferred;
  } else if ((ptr_ro_info->ptr_hspi->pTxBuffPtr != NULL) && (ptr_ro_info->ptr_hspi->TxXferSize != 0U)) {
    // If transmission was activated
    if (ptr_ro_info->ptr_hspi->hdmatx != NULL) {        // If DMA is used for Tx
      cnt_xferred = __HAL_DMA_GET_COUNTER(ptr_ro_info->ptr_hspi->hdmatx);
    } else {
      cnt_xferred = ptr_ro_info->ptr_hspi->TxXferCount;
    }

    cnt = (uint32_t)ptr_ro_info->ptr_hspi->TxXferSize - cnt_xferred;
  }

  return cnt;
}

/**
  \fn          int32_t SPIn_Control (const RO_Info_t * const ptr_ro_info, uint32_t control, uint32_t arg)
  \brief       Control SPI Interface.
  \param[in]   ptr_ro_info     Pointer to SPI RO info structure (RO_Info_t)
  \param[in]   control         Operation
  \param[in]   arg             Argument of operation (optional)
  \return      common \ref execution_status and driver specific \ref spi_execution_status
*/
static int32_t SPIn_Control (const RO_Info_t * const ptr_ro_info, uint32_t control, uint32_t arg) {
  GPIO_InitTypeDef GPIO_InitStruct;
  uint32_t         periph_clk;
  uint32_t         spi_clk;
  uint8_t          reconfigure_nss_pin;

  if (ptr_ro_info->ptr_rw_info->drv_status.powered == 0U) {
    return ARM_DRIVER_ERROR;
  }

  // Special handling for Abort Transfer command
  if ((control & ARM_SPI_CONTROL_Msk) == ARM_SPI_ABORT_TRANSFER) {
    if (HAL_SPI_Abort(ptr_ro_info->ptr_hspi) != HAL_OK) {
      return ARM_DRIVER_ERROR;
    }
    if (ptr_ro_info->ptr_hspi->hdmatx == NULL) {
      // If DMA is not used for Tx, clear TxXferSize for GetDataCount to work properly
      ptr_ro_info->ptr_hspi->TxXferSize = 0U;
    }
    if (ptr_ro_info->ptr_hspi->hdmarx == NULL) {
      // If DMA is not used for Rx, clear RxXferSize for GetDataCount to work properly
      ptr_ro_info->ptr_hspi->RxXferSize = 0U;
    }
    return ARM_DRIVER_OK;
  }

  // Check if peripheral is busy
  if (SPIn_GetStatus(ptr_ro_info).busy != 0U) {
    return ARM_DRIVER_ERROR_BUSY;
  }

  switch (control & ARM_SPI_CONTROL_Msk) {      // --- Control: Mode and Miscellaneous
                                                // --- Control Mode
    case ARM_SPI_MODE_INACTIVE:                 // Mode: SPI Inactive
      ptr_ro_info->ptr_rw_info->drv_status.configured = 0U;
      __HAL_SPI_DISABLE(ptr_ro_info->ptr_hspi);
      return ARM_DRIVER_OK;

    case ARM_SPI_MODE_MASTER:                   // Mode: SPI Master
      ptr_ro_info->ptr_hspi->Init.Mode      = SPI_MODE_MASTER;
      ptr_ro_info->ptr_hspi->Init.Direction = SPI_DIRECTION_2LINES;
      break;                                    // Continue configuring parameters after this switch block

    case ARM_SPI_MODE_SLAVE:                    // Mode: SPI Slave
      ptr_ro_info->ptr_hspi->Init.Mode      = SPI_MODE_SLAVE;
      ptr_ro_info->ptr_hspi->Init.Direction = SPI_DIRECTION_2LINES;
      break;                                    // Continue configuring parameters after this switch block

                                                // --- Control Miscellaneous
    case ARM_SPI_SET_BUS_SPEED:                 // Set Bus Speed in bps; arg = value
      periph_clk = SPIn_GetPeriphClock(ptr_ro_info);
      if (periph_clk == 0U) {
        // If peripheral clock is unknown
        return ARM_DRIVER_ERROR_UNSUPPORTED;
      }
      if      ((periph_clk >> 1) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;   }
      else if ((periph_clk >> 2) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;   }
      else if ((periph_clk >> 3) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;   }
      else if ((periph_clk >> 4) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;  }
      else if ((periph_clk >> 5) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;  }
      else if ((periph_clk >> 6) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;  }
      else if ((periph_clk >> 7) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; }
      else                               { return ARM_DRIVER_ERROR;                                                   }
      return ARM_DRIVER_OK;

    case ARM_SPI_GET_BUS_SPEED:                 // Get Bus Speed in bps
      periph_clk = SPIn_GetPeriphClock(ptr_ro_info);
      if (periph_clk == 0U) {
        // If peripheral clock is unknown
        return ARM_DRIVER_ERROR_UNSUPPORTED;
      }
      switch (ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler) {
        case SPI_BAUDRATEPRESCALER_2:   spi_clk = periph_clk >> 1; break;
        case SPI_BAUDRATEPRESCALER_4:   spi_clk = periph_clk >> 2; break;
        case SPI_BAUDRATEPRESCALER_8:   spi_clk = periph_clk >> 3; break;
        case SPI_BAUDRATEPRESCALER_16:  spi_clk = periph_clk >> 4; break;
        case SPI_BAUDRATEPRESCALER_32:  spi_clk = periph_clk >> 5; break;
        case SPI_BAUDRATEPRESCALER_64:  spi_clk = periph_clk >> 6; break;
        case SPI_BAUDRATEPRESCALER_128: spi_clk = periph_clk >> 7; break;
        case SPI_BAUDRATEPRESCALER_256: spi_clk = periph_clk >> 8; break;
        default:                        spi_clk = periph_clk;      break;
      }
      return ((int32_t)spi_clk);

    case ARM_SPI_SET_DEFAULT_TX_VALUE:          // Set default Transmit value; arg = value
      ptr_ro_info->ptr_rw_info->default_tx_value = arg;
      return ARM_DRIVER_OK;

    case ARM_SPI_CONTROL_SS:                    // Control Slave Select; arg: 0=inactive, 1=active
      if (ptr_ro_info->ptr_hspi->Init.NSS != SPI_NSS_SOFT) {
        // If slave select line is not configured as software controlled
        return ARM_DRIVER_ERROR;
      }

      switch (ptr_ro_info->ptr_hspi->Init.Mode) {
        case SPI_MODE_MASTER:                   // Master mode
          if (ptr_ro_info->ptr_nss_pin_config == NULL) {
            // If NSS pin is not available
            return ARM_DRIVER_ERROR;
          }

          if (arg == ARM_SPI_SS_INACTIVE) {
            // Inactive, set pin voltage level to high
            HAL_GPIO_WritePin(ptr_ro_info->ptr_nss_pin_config->ptr_port, ptr_ro_info->ptr_nss_pin_config->pin, GPIO_PIN_SET);
          } else {
            // Active, set pin voltage level to low
            HAL_GPIO_WritePin(ptr_ro_info->ptr_nss_pin_config->ptr_port, ptr_ro_info->ptr_nss_pin_config->pin, GPIO_PIN_RESET);
          }
          break;

        case SPI_MODE_SLAVE:                    // Slave mode
          if (arg == ARM_SPI_SS_ACTIVE) {
            // Inactive, set SSI bit in CR1 register to 0
            ptr_ro_info->ptr_hspi->Instance->CR1 &= ~SPI_CR1_SSI;
          } else {
            // Active, set SSI bit in CR1 register to 1
            ptr_ro_info->ptr_hspi->Instance->CR1 |=  SPI_CR1_SSI;
          }
          break;

        default:
          return ARM_DRIVER_ERROR;
      }
      return ARM_DRIVER_OK;

    default:                                    // Unknown Control
      return ARM_DRIVER_ERROR_PARAMETER;
  }

  // Configure all other parameters if command was to set Master (ARM_SPI_MODE_MASTER) or Slave mode (ARM_SPI_MODE_SLAVE)

  switch (control & ARM_SPI_FRAME_FORMAT_Msk) { // --- Mode Parameters: Frame Format
    case ARM_SPI_CPOL0_CPHA0:                   // Clock Polarity 0, Clock Phase 0 (default)
      ptr_ro_info->ptr_hspi->Init.TIMode      = SPI_TIMODE_DISABLE;
      ptr_ro_info->ptr_hspi->Init.CLKPhase    = SPI_PHASE_1EDGE;
      ptr_ro_info->ptr_hspi->Init.CLKPolarity = SPI_POLARITY_LOW;
      break;

    case ARM_SPI_CPOL0_CPHA1:                   // Clock Polarity 0, Clock Phase 1
      ptr_ro_info->ptr_hspi->Init.TIMode      = SPI_TIMODE_DISABLE;
      ptr_ro_info->ptr_hspi->Init.CLKPhase    = SPI_PHASE_2EDGE;
      ptr_ro_info->ptr_hspi->Init.CLKPolarity = SPI_POLARITY_LOW;
      break;

    case ARM_SPI_CPOL1_CPHA0:                   // Clock Polarity 0, Clock Phase 1
      ptr_ro_info->ptr_hspi->Init.TIMode      = SPI_TIMODE_DISABLE;
      ptr_ro_info->ptr_hspi->Init.CLKPhase    = SPI_PHASE_1EDGE;
      ptr_ro_info->ptr_hspi->Init.CLKPolarity = SPI_POLARITY_HIGH;
      break;

    case ARM_SPI_CPOL1_CPHA1:                   // Clock Polarity 1, Clock Phase 1
      ptr_ro_info->ptr_hspi->Init.TIMode      = SPI_TIMODE_DISABLE;
      ptr_ro_info->ptr_hspi->Init.CLKPhase    = SPI_PHASE_2EDGE;
      ptr_ro_info->ptr_hspi->Init.CLKPolarity = SPI_POLARITY_HIGH;
      break;

    case ARM_SPI_TI_SSI:                        // Texas Instruments Frame Format
      ptr_ro_info->ptr_hspi->Init.TIMode      = SPI_TIMODE_ENABLE;
      break;

    case ARM_SPI_MICROWIRE:                     // National Semiconductor Microwire Frame Format
      return ARM_SPI_ERROR_FRAME_FORMAT;

    default:
      return ARM_SPI_ERROR_FRAME_FORMAT;
  }

  switch (control & ARM_SPI_DATA_BITS_Msk) {    // --- Mode Parameters: Data Bits
    case ARM_SPI_DATA_BITS(8U):  ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_8BIT;  break;
    case ARM_SPI_DATA_BITS(16U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_16BIT; break;
#ifdef SPI_DATASIZE_4BIT
    case ARM_SPI_DATA_BITS(4U):  ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_4BIT;  break;
#endif
#ifdef SPI_DATASIZE_5BIT
    case ARM_SPI_DATA_BITS(5U):  ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_5BIT;  break;
#endif
#ifdef SPI_DATASIZE_6BIT
    case ARM_SPI_DATA_BITS(6U):  ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_6BIT;  break;
#endif
#ifdef SPI_DATASIZE_7BIT
    case ARM_SPI_DATA_BITS(7U):  ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_7BIT;  break;
#endif
#ifdef SPI_DATASIZE_9BIT
    case ARM_SPI_DATA_BITS(9U):  ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_9BIT;  break;
#endif
#ifdef SPI_DATASIZE_10BIT
    case ARM_SPI_DATA_BITS(10U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_10BIT; break;
#endif
#ifdef SPI_DATASIZE_11BIT
    case ARM_SPI_DATA_BITS(11U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_11BIT; break;
#endif
#ifdef SPI_DATASIZE_12BIT
    case ARM_SPI_DATA_BITS(12U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_12BIT; break;
#endif
#ifdef SPI_DATASIZE_13BIT
    case ARM_SPI_DATA_BITS(13U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_13BIT; break;
#endif
#ifdef SPI_DATASIZE_14BIT
    case ARM_SPI_DATA_BITS(14U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_14BIT; break;
#endif
#ifdef SPI_DATASIZE_15BIT
    case ARM_SPI_DATA_BITS(15U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_15BIT; break;
#endif
#ifdef SPI_DATASIZE_17BIT
    case ARM_SPI_DATA_BITS(17U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_17BIT; break;
#endif
#ifdef SPI_DATASIZE_18BIT
    case ARM_SPI_DATA_BITS(18U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_18BIT; break;
#endif
#ifdef SPI_DATASIZE_19BIT
    case ARM_SPI_DATA_BITS(19U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_19BIT; break;
#endif
#ifdef SPI_DATASIZE_20BIT
    case ARM_SPI_DATA_BITS(20U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_20BIT; break;
#endif
#ifdef SPI_DATASIZE_21BIT
    case ARM_SPI_DATA_BITS(21U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_21BIT; break;
#endif
#ifdef SPI_DATASIZE_22BIT
    case ARM_SPI_DATA_BITS(22U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_22BIT; break;
#endif
#ifdef SPI_DATASIZE_23BIT
    case ARM_SPI_DATA_BITS(23U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_23BIT; break;
#endif
#ifdef SPI_DATASIZE_24BIT
    case ARM_SPI_DATA_BITS(24U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_24BIT; break;
#endif
#ifdef SPI_DATASIZE_25BIT
    case ARM_SPI_DATA_BITS(25U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_25BIT; break;
#endif
#ifdef SPI_DATASIZE_26BIT
    case ARM_SPI_DATA_BITS(26U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_26BIT; break;
#endif
#ifdef SPI_DATASIZE_27BIT
    case ARM_SPI_DATA_BITS(27U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_27BIT; break;
#endif
#ifdef SPI_DATASIZE_28BIT
    case ARM_SPI_DATA_BITS(28U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_28BIT; break;
#endif
#ifdef SPI_DATASIZE_29BIT
    case ARM_SPI_DATA_BITS(29U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_29BIT; break;
#endif
#ifdef SPI_DATASIZE_30BIT
    case ARM_SPI_DATA_BITS(30U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_30BIT; break;
#endif
#ifdef SPI_DATASIZE_31BIT
    case ARM_SPI_DATA_BITS(31U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_31BIT; break;
#endif
#ifdef SPI_DATASIZE_32BIT
    case ARM_SPI_DATA_BITS(32U): ptr_ro_info->ptr_hspi->Init.DataSize = SPI_DATASIZE_32BIT; break;
#endif
    default:
      return ARM_SPI_ERROR_DATA_BITS;
  }

                                                // --- Mode Parameters: Bit Order
  if ((control & ARM_SPI_BIT_ORDER_Msk) == ARM_SPI_LSB_MSB) {
    ptr_ro_info->ptr_hspi->Init.FirstBit = SPI_FIRSTBIT_LSB;
  } else {
    ptr_ro_info->ptr_hspi->Init.FirstBit = SPI_FIRSTBIT_MSB;
  }

  reconfigure_nss_pin = 0U;                                     // --- Mode Parameters: Slave Select Mode
  if (ptr_ro_info->ptr_hspi->Init.Mode == SPI_MODE_MASTER) {    // If mode is Master
    switch (control & ARM_SPI_SS_MASTER_MODE_Msk) {             // --- Mode Parameters: Slave Select Mode
      case ARM_SPI_SS_MASTER_UNUSED:                            // SPI Slave Select when Master: Not used (default)
        ptr_ro_info->ptr_hspi->Init.NSS      = SPI_NSS_SOFT;
#ifdef  SPI_NSS_PULSE_DISABLE
        ptr_ro_info->ptr_hspi->Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
#endif
        if (ptr_ro_info->ptr_nss_pin_config != NULL) {
          // Unconfigure NSS pin
          HAL_GPIO_DeInit(ptr_ro_info->ptr_nss_pin_config->ptr_port, ptr_ro_info->ptr_nss_pin_config->pin);
        }
        break;

      case ARM_SPI_SS_MASTER_SW:                                // SPI Slave Select when Master: Software controlled
        if (ptr_ro_info->ptr_nss_pin_config == NULL) {
          // If NSS pin is not available
          return ARM_SPI_ERROR_SS_MODE;
        }

        ptr_ro_info->ptr_hspi->Init.NSS      = SPI_NSS_SOFT;
#ifdef  SPI_NSS_PULSE_DISABLE
        ptr_ro_info->ptr_hspi->Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
#endif

        // Prepare NSS pin configuration for GPIO output mode
        GPIO_InitStruct.Mode     = GPIO_MODE_OUTPUT_PP;
        reconfigure_nss_pin      = 1U;
        break;

      case ARM_SPI_SS_MASTER_HW_OUTPUT:                         // SPI Slave Select when Master: Hardware controlled Output
        if (ptr_ro_info->ptr_nss_pin_config == NULL) {
          // If NSS pin is not available
          return ARM_SPI_ERROR_SS_MODE;
        }

        ptr_ro_info->ptr_hspi->Init.NSS      = SPI_NSS_HARD_OUTPUT;
#ifdef  SPI_NSS_PULSE_DISABLE
        ptr_ro_info->ptr_hspi->Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
#endif

        // Prepare NSS pin configuration for alternate function mode
        GPIO_InitStruct.Mode     = GPIO_MODE_AF_PP;
        reconfigure_nss_pin      = 1U;
        break;

      case ARM_SPI_SS_MASTER_HW_INPUT:                          // SPI Slave Select when Master: Hardware monitored Input
        if (ptr_ro_info->ptr_nss_pin_config == NULL) {
          // If NSS pin is not available
          return ARM_SPI_ERROR_SS_MODE;
        }

        ptr_ro_info->ptr_hspi->Init.NSS      = SPI_NSS_HARD_INPUT;
#ifdef  SPI_NSS_PULSE_DISABLE
        ptr_ro_info->ptr_hspi->Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
#endif

        // Prepare NSS pin configuration for alternate function mode
        GPIO_InitStruct.Mode     = GPIO_MODE_AF_PP;
        reconfigure_nss_pin      = 1U;
        break;

      default:
        return ARM_SPI_ERROR_SS_MODE;
    }
  } else if (ptr_ro_info->ptr_hspi->Init.Mode == SPI_MODE_SLAVE) {  // If mode is Slave
    switch (control & ARM_SPI_SS_SLAVE_MODE_Msk) {              // --- Mode Parameters: Slave Select Mode
      case ARM_SPI_SS_SLAVE_HW:                                 // SPI Slave Select when Slave: Hardware monitored (default)
        if (ptr_ro_info->ptr_nss_pin_config == NULL) {
          // If NSS pin is not available
          return ARM_SPI_ERROR_SS_MODE;
        }

        ptr_ro_info->ptr_hspi->Init.NSS      = SPI_NSS_HARD_INPUT;
#ifdef  SPI_NSS_PULSE_DISABLE
        ptr_ro_info->ptr_hspi->Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
#endif

        // Prepare NSS pin configuration for alternate function mode
        GPIO_InitStruct.Mode     = GPIO_MODE_AF_PP;
        reconfigure_nss_pin      = 1U;
        break;

      case ARM_SPI_SS_SLAVE_SW:                                 // SPI Slave Select when Slave: Software controlled
        ptr_ro_info->ptr_hspi->Init.NSS       = SPI_NSS_SOFT;
#ifdef  SPI_NSS_PULSE_DISABLE
        ptr_ro_info->ptr_hspi->Init.NSSPMode  = SPI_NSS_PULSE_DISABLE;
#endif
        if (ptr_ro_info->ptr_nss_pin_config != NULL) {
          // Unconfigure NSS pin
          HAL_GPIO_DeInit(ptr_ro_info->ptr_nss_pin_config->ptr_port, ptr_ro_info->ptr_nss_pin_config->pin);
        }
        break;

      default:
        return ARM_SPI_ERROR_SS_MODE;
    }
  }

  // Configure Bus Speed, only for Master mode
  if (ptr_ro_info->ptr_hspi->Init.Mode == SPI_MODE_MASTER) {
    periph_clk = SPIn_GetPeriphClock(ptr_ro_info);
    if (periph_clk != 0U) {
      // If peripheral clock is valid, if peripheral clock is not valid or unknown then clock reconfiguration will be skipped
      if      ((periph_clk >> 1) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;   }
      else if ((periph_clk >> 2) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;   }
      else if ((periph_clk >> 3) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;   }
      else if ((periph_clk >> 4) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;  }
      else if ((periph_clk >> 5) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;  }
      else if ((periph_clk >> 6) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;  }
      else if ((periph_clk >> 7) <= arg) { ptr_ro_info->ptr_hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; }
      else                               { return ARM_DRIVER_ERROR;                                                   }
    }
  }

  // Reconfigure DMA
  if (ptr_ro_info->ptr_hspi->hdmarx != NULL) {      // If DMA is used for Rx
    if ((control & ARM_SPI_DATA_BITS_Msk) > ARM_SPI_DATA_BITS(16U)) {
#if defined(DMA_MDATAALIGN_WORD)
      ptr_ro_info->ptr_hspi->hdmarx->Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
      ptr_ro_info->ptr_hspi->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
#else
      ptr_ro_info->ptr_hspi->hdmarx->Init.SrcDataWidth        = DMA_SRC_DATAWIDTH_WORD;
      ptr_ro_info->ptr_hspi->hdmarx->Init.DestDataWidth       = DMA_DEST_DATAWIDTH_WORD;
#endif
    } else if ((control & ARM_SPI_DATA_BITS_Msk) > ARM_SPI_DATA_BITS(8U)) {
#if defined(DMA_MDATAALIGN_HALFWORD)
      ptr_ro_info->ptr_hspi->hdmarx->Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
      ptr_ro_info->ptr_hspi->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
#else
      ptr_ro_info->ptr_hspi->hdmarx->Init.SrcDataWidth        = DMA_SRC_DATAWIDTH_HALFWORD;
      ptr_ro_info->ptr_hspi->hdmarx->Init.DestDataWidth       = DMA_DEST_DATAWIDTH_HALFWORD;
#endif
    } else {
#if defined(DMA_MDATAALIGN_BYTE)
      ptr_ro_info->ptr_hspi->hdmarx->Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
      ptr_ro_info->ptr_hspi->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
#else
      ptr_ro_info->ptr_hspi->hdmarx->Init.SrcDataWidth        = DMA_SRC_DATAWIDTH_BYTE;
      ptr_ro_info->ptr_hspi->hdmarx->Init.DestDataWidth       = DMA_DEST_DATAWIDTH_BYTE;
#endif
    }

    // Reconfigure DMA for Rx
    if (HAL_DMA_Init(ptr_ro_info->ptr_hspi->hdmarx) != HAL_OK) {
      return ARM_DRIVER_ERROR;
    }
  }

  if (ptr_ro_info->ptr_hspi->hdmatx != NULL) {      // If DMA is used for Tx
    if ((control & ARM_SPI_DATA_BITS_Msk) > ARM_SPI_DATA_BITS(16U)) {
#if defined(DMA_MDATAALIGN_WORD)
      ptr_ro_info->ptr_hspi->hdmatx->Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
      ptr_ro_info->ptr_hspi->hdmatx->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
#else
      ptr_ro_info->ptr_hspi->hdmatx->Init.SrcDataWidth        = DMA_SRC_DATAWIDTH_WORD;
      ptr_ro_info->ptr_hspi->hdmatx->Init.DestDataWidth       = DMA_DEST_DATAWIDTH_WORD;
#endif
    } else if ((control & ARM_SPI_DATA_BITS_Msk) > ARM_SPI_DATA_BITS(8U)) {
#if defined(DMA_MDATAALIGN_HALFWORD)
      ptr_ro_info->ptr_hspi->hdmatx->Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
      ptr_ro_info->ptr_hspi->hdmatx->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
#else
      ptr_ro_info->ptr_hspi->hdmatx->Init.SrcDataWidth        = DMA_SRC_DATAWIDTH_HALFWORD;
      ptr_ro_info->ptr_hspi->hdmatx->Init.DestDataWidth       = DMA_DEST_DATAWIDTH_HALFWORD;
#endif
    } else {
#if defined(DMA_MDATAALIGN_BYTE)
      ptr_ro_info->ptr_hspi->hdmatx->Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
      ptr_ro_info->ptr_hspi->hdmatx->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
#else
      ptr_ro_info->ptr_hspi->hdmatx->Init.SrcDataWidth        = DMA_SRC_DATAWIDTH_BYTE;
      ptr_ro_info->ptr_hspi->hdmatx->Init.DestDataWidth       = DMA_DEST_DATAWIDTH_BYTE;
#endif
    }

    // Reconfigure DMA for Tx
    if (HAL_DMA_Init(ptr_ro_info->ptr_hspi->hdmatx) != HAL_OK) {
      return ARM_DRIVER_ERROR;
    }
  }

  // Reconfigure SPI
  if (HAL_SPI_Init(ptr_ro_info->ptr_hspi) != HAL_OK) {
    return ARM_DRIVER_ERROR;
  }

  // Reconfigure NSS pin
  if (reconfigure_nss_pin != 0U) {
    GPIO_InitStruct.Pin       = ptr_ro_info->ptr_nss_pin_config->pin;
    GPIO_InitStruct.Pull      = ptr_ro_info->ptr_nss_pin_config->pull;
    GPIO_InitStruct.Speed     = ptr_ro_info->ptr_nss_pin_config->speed;
    GPIO_InitStruct.Alternate = ptr_ro_info->ptr_nss_pin_config->af;
    HAL_GPIO_Init(ptr_ro_info->ptr_nss_pin_config->ptr_port, &GPIO_InitStruct);
  }

  // Set driver status to configured
  ptr_ro_info->ptr_rw_info->drv_status.configured = 1U;

  return ARM_DRIVER_OK;
}

/**
  \fn          ARM_SPI_STATUS SPIn_GetStatus (const RO_Info_t * const ptr_ro_info)
  \brief       Get SPI status.
  \param[in]   ptr_ro_info     Pointer to SPI RO info structure (RO_Info_t)
  \return      SPI status \ref ARM_SPI_STATUS
*/
static ARM_SPI_STATUS SPIn_GetStatus (const RO_Info_t * const ptr_ro_info) {
  ARM_SPI_STATUS status;
  uint32_t       error;

  // Clear status structure
  memset(&status, 0, sizeof(ARM_SPI_STATUS));

  error = HAL_SPI_GetError(ptr_ro_info->ptr_hspi);

  // Process HAL state
  switch (HAL_SPI_GetState(ptr_ro_info->ptr_hspi)) {
    case HAL_SPI_STATE_BUSY:            // An internal process is ongoing
    case HAL_SPI_STATE_BUSY_TX:         // Data Transmission process is ongoing
    case HAL_SPI_STATE_BUSY_RX:         // Data Reception process is ongoing
    case HAL_SPI_STATE_BUSY_TX_RX:      // Data Transmission and Reception process is ongoing
    case HAL_SPI_STATE_ABORT:           // SPI abort is ongoing
      status.busy = 1U;
      break;

    case HAL_SPI_STATE_RESET:           // Peripheral not Initialized
    case HAL_SPI_STATE_READY:           // Peripheral Initialized and ready for use
    case HAL_SPI_STATE_ERROR:           // SPI error state
    default:
      // Not busy related
      break;
  }

  // Process HAL errors status
  if ((error & HAL_SPI_ERROR_OVR)  != 0U) {
    status.data_lost = 1U;
  }
  if ((error & HAL_SPI_ERROR_MODF) != 0U) {
    status.mode_fault = 1U;
  }

  return status;
}

// HAL callback functions ******************************************************

/**
  \fn          void HAL_SPI_TxCpltCallback (SPI_HandleTypeDef *hspi)
  \brief       Tx Transfer completed callback.
  \param[in]   hspi     SPI handle
  */
void HAL_SPI_TxCpltCallback (SPI_HandleTypeDef *hspi) {
  const RO_Info_t *ptr_ro_info;

  ptr_ro_info = SPI_GetInfo(hspi);

  if (ptr_ro_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_hspi == NULL) {
    return;
  }

#ifndef SPI_VARIANT_NO_HAL_EX
  if ((ptr_ro_info->ptr_hspi->pRxBuffPtr != NULL) && (ptr_ro_info->ptr_hspi->RxXferSize != 0U) && (ptr_ro_info->ptr_hspi->hdmarx == NULL)) {
    // If DMA is not used for Rx during Tx, flush Rx after transmission
    (void)HAL_SPIEx_FlushRxFifo(ptr_ro_info->ptr_hspi);
  }
#endif

  if (ptr_ro_info->ptr_rw_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info->cb_event == NULL) {
    return;
  }

  ptr_ro_info->ptr_rw_info->cb_event(ARM_SPI_EVENT_TRANSFER_COMPLETE);
}

/**
  \fn          void HAL_SPI_RxCpltCallback (SPI_HandleTypeDef *hspi)
  \brief       Rx Transfer completed callback.
  \param[in]   hspi     SPI handle
  */
void HAL_SPI_RxCpltCallback (SPI_HandleTypeDef *hspi) {
  const RO_Info_t *ptr_ro_info;

  ptr_ro_info = SPI_GetInfo(hspi);

  if (ptr_ro_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info->cb_event == NULL) {
    return;
  }

  ptr_ro_info->ptr_rw_info->cb_event(ARM_SPI_EVENT_TRANSFER_COMPLETE);
}

/**
  \fn          void HAL_SPI_TxRxCpltCallback (SPI_HandleTypeDef *hspi)
  \brief       Tx and Rx Transfer completed callback.
  \param[in]   hspi     SPI handle
  */
void HAL_SPI_TxRxCpltCallback (SPI_HandleTypeDef *hspi) {
  const RO_Info_t *ptr_ro_info;

  ptr_ro_info = SPI_GetInfo(hspi);

  if (ptr_ro_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info->cb_event == NULL) {
    return;
  }

  ptr_ro_info->ptr_rw_info->cb_event(ARM_SPI_EVENT_TRANSFER_COMPLETE);
}

/**
  \fn          void HAL_SPI_ErrorCallback (SPI_HandleTypeDef *hspi)
  \brief       SPI error callback.
  \param[in]   hspi     SPI handle
  */
void HAL_SPI_ErrorCallback (SPI_HandleTypeDef *hspi) {
  const RO_Info_t *ptr_ro_info;
        uint32_t   error;
        uint32_t   event;

  ptr_ro_info = SPI_GetInfo(hspi);

  if (ptr_ro_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info->cb_event == NULL) {
    return;
  }

  error = HAL_SPI_GetError(hspi);
  event = 0U;

  if ((error & HAL_SPI_ERROR_MODF) != 0U) {
    event |= ARM_SPI_EVENT_MODE_FAULT;
  }

  if ((error & HAL_SPI_ERROR_OVR) != 0U) {
    event |= ARM_SPI_EVENT_DATA_LOST;
  }

  if (event != 0U) {
    ptr_ro_info->ptr_rw_info->cb_event(event);
  }
}

// Local driver functions definitions (for instances)
#ifdef MX_SPI1
FUNCS_DEFINE(1)
#endif
#ifdef MX_SPI2
FUNCS_DEFINE(2)
#endif
#ifdef MX_SPI3
FUNCS_DEFINE(3)
#endif
#ifdef MX_SPI4
FUNCS_DEFINE(4)
#endif
#ifdef MX_SPI5
FUNCS_DEFINE(5)
#endif
#ifdef MX_SPI6
FUNCS_DEFINE(6)
#endif
#ifdef MX_SPI7
FUNCS_DEFINE(7)
#endif
#ifdef MX_SPI8
FUNCS_DEFINE(8)
#endif

// Global driver structures ****************************************************

#ifdef MX_SPI1
SPI_DRIVER(1)
#endif
#ifdef MX_SPI2
SPI_DRIVER(2)
#endif
#ifdef MX_SPI3
SPI_DRIVER(3)
#endif
#ifdef MX_SPI4
SPI_DRIVER(4)
#endif
#ifdef MX_SPI5
SPI_DRIVER(5)
#endif
#ifdef MX_SPI6
SPI_DRIVER(6)
#endif
#ifdef MX_SPI7
SPI_DRIVER(7)
#endif
#ifdef MX_SPI8
SPI_DRIVER(8)
#endif

#endif  // DRIVER_CONFIG_VALID

/*! \endcond */
